/*
 * SharedMemory.h
 *
 *  Created on: 24/10/2013
 *      Author: gonzalo
 */

#ifndef SHAREDMEMORYINTERFACE_H_
#define SHAREDMEMORYINTERFACE_H_

#include "../IPC/SharedMemory.h"
#include "../IPC/Semaphore.h"
#include "DSMManager.h"

typedef int SHMUid;

namespace ipc {

template <class T>
class SharedMemoryInterface {
private:
	SharedMemory<T> sharedMemory;
	Semaphore mutex;
	SHMUid shmUid;
	SharedMemoryInterface(SharedMemory<T> shMem, Semaphore mutex, SHMUid uid = 0);

public:

	SharedMemoryInterface();
	/*
	 *  Crea la estructura básica de la memoria compartida.
	 *  Si ya existe devuelve error.
	 *  @param size: tamanio del array en caso de querer que la SHMem sea un array,
	 *  caso contrario dejar el default, 1.
	 */
	static SharedMemoryInterface<T> create(std::string file, char c, int size = 1);

	/*
	 *  Obtiene la estructura de la memoria compartida, si no existe lo crea.
	 */
	static SharedMemoryInterface<T> get(std::string file, char c, SHMUid uid = 0, int size = 1);

	/*
	 *  Destruye la estructura de la memoria compartida.
	 */
	static void destroy(std::string file, char c);

	/*
	 *  Se bloquea hasta que la memoria sea accesible.
	 */
	void lock();

	/*
	 * Desbloquea la memoria para ser accedida por otros usuarios.
	 */
	void unlock();

	/*
	 * Lee los datos de la memoria en a posicion indicada por index,
	 * opcionalmente haciendo un lock/unlock sobre la memoria.
	 * En el último caso la llamada será bloqueante hasta que se
	 * encuentre disponible la memoria.
	 *
	 * @param int index: posición a leer si es array. Por defecto 0.
	 * @param lock: opcionalmente, también realiza un lock y unlock.
	 *
	 * @return el valor leido
	 */
	T read(int index = 0, bool lock = false);

	/*
	 * Escribe los datos del buffer en la memoria, opcionalmente haciendo un lock/unlock
	 * sobre la memoria. En el último caso la llamada será bloqueante hasta que se
	 * encuentre disponible la memoria.
	 * @param T value : dato a escribir
	 * @param int index: posicion de la memoria si es array, por defecto 0.
	 * @param lock: opcionalmente, también realiza un lock y unlock.
	 */
	void write(T value, int index = 0, bool lock = false);

	~SharedMemoryInterface();
};


template <class T>
SharedMemoryInterface<T>::SharedMemoryInterface() {}

template <class T>
SharedMemoryInterface<T>::SharedMemoryInterface(SharedMemory<T> shMem, Semaphore mutex, SHMUid uid) {
	this->mutex = mutex;
	this->sharedMemory = shMem;
	this->shmUid = uid;
}

template <class T>
SharedMemoryInterface<T> SharedMemoryInterface<T>::create(std::string file, char c, int size) {
	SharedMemory<T> shMem = SharedMemory<T>::create( file, c, size );
	Semaphore mutex = Semaphore::create( file, c, 1 );
	mutex.initialize(1);
	return SharedMemoryInterface<T>(shMem, mutex);
}

template <class T>
SharedMemoryInterface<T> SharedMemoryInterface<T>::get(std::string file, char c, SHMUid uid, int size) {
	SharedMemory<T> shMem = SharedMemory<T>::get( file, c, size );
	Semaphore mutex = Semaphore::get( file, c, 1 );
	mutex.initialize(1);
	return SharedMemoryInterface<T>(shMem, mutex, uid);
}

template <class T>
void SharedMemoryInterface<T>::destroy(std::string file, char c) {
	Semaphore::destroy( file, c);
	SharedMemory<T>::destroy( file, c );
}

template <class T>
void SharedMemoryInterface<T>::lock() {
	if (shmUid == 0) {
		this->mutex.wait();
	} else {
	     DSMManager::pedir( shmUid );
	}
}

template <class T>
void SharedMemoryInterface<T>::unlock() {
	if (shmUid == 0) {
		this->mutex.signal();
	} else {
	     DSMManager::liberar( shmUid );
	}
}

template <class T>
T SharedMemoryInterface<T>::read(int index, bool lock) {
	if (lock) this->lock();
	T val = this->sharedMemory.read(index);
	if (lock) this->unlock();
	return val;
}

template <class T>
void SharedMemoryInterface<T>::write(T value, int index, bool lock) {
	if (lock) this->lock();
	this->sharedMemory.write(value, index);
	if (lock) this->unlock();
}

template <class T>
SharedMemoryInterface<T>::~SharedMemoryInterface() {}

} /* namespace ipc */
#endif /* SHAREDMEMORYINTERFACE_H_ */
